/*
 * BPlusTreeTemplate.h
 *
 *  Created on: Nov 4, 2012
 *      Author: Federico Orquera
 */

#ifndef BPLUSTREETEMPLATE_H_
#define BPLUSTREETEMPLATE_H_

#include "bplusnodes/NewInternal.h"
#include "bplusfilehandler/NewHandlerInterface.h"

template <
	typename keyTemplate,
	typename valueTemplate,
	bool keyComparator(
		const keyTemplate& key1,
		const keyTemplate& key2
	)
>
class BPlusTreeTemplate {
public:

	typedef keyTemplate		BPKey_t;
	typedef valueTemplate	BPValue_t;

	typedef NewNode     <BPKey_t, BPValue_t, keyComparator> Node;
	typedef NewLeaf     <BPKey_t, BPValue_t, keyComparator> Leaf;
	typedef NewInternal <BPKey_t, BPValue_t, keyComparator> Internal;

	typedef typename Node::NodeLevel    NodeLevel_t;
	typedef typename Node::NodeID       NodeID_t;
	typedef typename Node::searchResult searchResult_t;
	typedef typename Node::register_ptr	register_ptr;

	typedef NewHandlerInterface<NodeID_t,NodeLevel_t,BPKey_t,BPValue_t,keyComparator> HandlerInterface;

	typedef typename HandlerInterface::node_ptr node_ptr;

	BPlusTreeTemplate(HandlerInterface *newHandler){
		this->handler = newHandler;
		rootNode = handler -> getRoot();
		firstSearch = true;
	}

	virtual ~BPlusTreeTemplate(){}

	virtual void InsertRegister( const register_ptr& registro ){

		switch( rootNode->InsertRegister( registro ) ){

			// Si no hubo cambios, terminar.
			case NO_CHANGE:
				break;

			// si hubo cambios, grabar.
			case UPDATED:
				handler->saveNode(rootNode);
				break;

			// si hubo overflow, resolver overflow de raiz!!!
			case NODE_OVERFLOW:
				this->solveOverflow();
				break;

			default:
				break;
		}

	}

	virtual void UpdateRegister( const register_ptr& registro ){

		switch( rootNode->UpdateRegister( registro ) ){

			// Si no hubo cambios, terminar.
			case NO_CHANGE:
				break;

			// si hubo cambios, grabar.
			case UPDATED:
				handler->saveNode(rootNode);
				break;

			// si hubo overflow, resolver overflow de raiz!!!
			case OVERFLOW:
				this->solveOverflow();
				break;

			default:
				break;
		}

	}

	void LoadRegister( const register_ptr& registro ){

		switch( rootNode->LoadRegister( registro ) ){

			// Si no hubo cambios, terminar.
			case NodeResulMessages::NO_CHANGE:
				break;

			// si hubo cambios, grabar.
			case NodeResulMessages::UPDATED:
				handler->saveNode(rootNode);
				break;

			// si hubo overflow, resolver overflow de raiz!!!
			case NodeResulMessages::NODE_OVERFLOW:
				this->solveLoadOverflow(registro);
				break;

			default:
				break;
		}

	}

	void DeleteRegister( const BPKey_t& key ){}

	searchResult_t begin( ){
		return rootNode->begin();
	}

	searchResult_t SearchRegister( const BPKey_t& key ){
		return rootNode->SearchRegister(key);
	}

	searchResult_t SearchNextRegister( const searchResult_t& result ){

		if(result.endOfList) return result;

		if(firstSearch || result.nextNode != newSearchNode->getNodeID()) newSearchNode = handler->getNode(result.nextNode);

		firstSearch = false;

		//node_ptr newSearchNode = handler->getNode(result.nextNode);

		searchResult_t newResult = ((Leaf*) (newSearchNode.get()))->SearchNextRegister(result.result->getKey());

		return newResult;
	}

	void MostrarContenido(){this->MostrarContenido("");}

	void MostrarContenidoReducido(){this->MostrarContenidoReducido("");}

	void MostrarContenido( const std::string& separator ){}

	void MostrarContenidoReducido(const std::string& separator ){

		// ID: LVL | SIZE | SON_1(KEY_1)SON_2(KEY_N)SON_N

		rootNode->MostrarContenidoReducido(separator);
	}

protected:

	//No Perisistir
	HandlerInterface  	*handler;

private:

	//No Perisistir
	node_ptr			rootNode;
	node_ptr 			newSearchNode;
	bool				firstSearch;

	void createNewRoot( const NodeID_t& firstSon ){

		// Pido una Nueva raiz.
		node_ptr newRootNode = handler->getNewRoot(rootNode->getLevel()+1);

		// Agrego el primer hijo.
		((Internal*) (newRootNode.get()))->setFirstSon(firstSon);

		// Guardo los cambios de la raiz.
		handler->saveNode(newRootNode);

		// Asigno nueva raiz
		rootNode = newRootNode;

	}

	void solveOverflow(){

		node_ptr oldRootNode = this->rootNode;

		// El arbol siempre crece para arriba!
		this->createNewRoot( this->rootNode->getNodeID() );

		// Uso el solve de nodo interno.
		((Internal*) rootNode.get())->solveOverflow(oldRootNode);

		handler->saveNode(this->rootNode);

	}

	void solveLoadOverflow(const register_ptr& registro){

		// El arbol siempre crece para arriba!
		this->createNewRoot( this->rootNode->getNodeID() );

		// Uso el solve de nodo interno.
		this->rootNode->LoadRegister( registro );

	}
};


#endif /* BPLUSTREETEMPLATE_H_ */
